package com.piece.core.mongo.config;

import com.mongodb.ConnectionString;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.piece.core.framework.properties.ApplicationProperties;
import com.piece.core.mongo.properties.MongoProperties;
import com.piece.core.framework.support.convert.TimestampConverter;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.mongo.service.MongoMessageLogService;
import com.piece.core.mongo.util.MongoMessageOperator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.convert.*;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import java.util.ArrayList;
import java.util.List;

/**
 * mongo 配置类
 */
@EnableConfigurationProperties(MongoProperties.class)
public class MongoAutoConfigure {

    @Bean
    @Qualifier("mongoDbFactory")
    public MongoDatabaseFactory mongoDbFactory(MongoProperties mongoProperties) {
        String uri = getURIString(mongoProperties);
        ConnectionString connectionString = new ConnectionString(uri);
        MongoDatabaseFactory factory = new SimpleMongoClientDatabaseFactory(connectionString);
        return factory;
    }

    private String getURIString(MongoProperties mongoProperties) {
        String uri = mongoProperties.getUri();
        String database = mongoProperties.getDatabase();
        String username = mongoProperties.getUsername();
        String password = mongoProperties.getPassword();

        if (StringUtil.isNotEmpty(username) && StringUtil.isNotEmpty(password)) {
            uri = "mongodb://" +username + ":" + password + "@" + uri;
        } else {
            uri = "mongodb://" + uri;
        }
        uri += "/" + database;

        String options = "";
        if (StringUtil.isNotEmpty(mongoProperties.getReplicaSet())) {
            options += "&replicaSet=" + mongoProperties.getReplicaSet();
        }
        options += "&minPoolSize=" + mongoProperties.getMinPoolSize();
        options += "&maxPoolSize=" + mongoProperties.getMaxPoolSize();
        options += "&maxIdleTimeMs=" + mongoProperties.getMaxIdleTimeMs();
        options += "&maxLifeTimeMs=" + mongoProperties.getMaxLifeTimeMs();
        options += "&waitQueueTimeoutMs=" + mongoProperties.getWaitQueueTimeoutMs();
        options += "&connectTimeoutMs=" + mongoProperties.getConnectTimeoutMs();
        options += "&socketTimeoutMs=" + mongoProperties.getSocketTimeoutMs();
        options += "&ssl=" + mongoProperties.getSsl().toString();
        options += "&sslInvalidHostnameAllowed=" + mongoProperties.getSslInvalidHostnameAllowed().toString();
        options += "&heartbeatFrequencyMs=" + mongoProperties.getHeartbeatFrequencyMs();
        options = options.replaceFirst("&", "?");
        return uri + options;
    }

    /**
     * 添加字段转换器
     */
    @Bean
    public MongoCustomConversions customConversions() {
        List<Converter<?, ?>> converterList = new ArrayList<>();
        converterList.add(new TimestampConverter());
        return new MongoCustomConversions(converterList);
    }

    @Bean
    public MongoMappingContext mongoMappingContext() {
        return new MongoMappingContext();
    }

    @Bean
    public MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory mongoDbFactory, MongoMappingContext mongoMappingContext, MongoCustomConversions customConversions) {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory);
        MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, mongoMappingContext);
        mappingConverter.setCustomConversions(customConversions);
        // 不存储_class字段
        mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
        return mappingConverter;
    }

    @Bean("mongoTemplate")
    public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDbFactory, MappingMongoConverter converter) {
        return new MongoTemplate(mongoDbFactory, converter);
    }

    @Bean("gridFsTemplate")
    public GridFsTemplate gridFsTemplate(MongoDatabaseFactory mongoDbFactory, MappingMongoConverter converter) {
        return new GridFsTemplate(mongoDbFactory, converter);
    }

    @Bean("gridFSBucket")
    public GridFSBucket gridFSBucket(MongoDatabaseFactory mongoDbFactory) {
        GridFSBucket bucket = GridFSBuckets.create(mongoDbFactory.getMongoDatabase());
        return bucket;
    }

    @Bean("mongoMessageOperator")
    public MongoMessageOperator mongoMessageOperator(ApplicationProperties applicationProperties, @Lazy MongoMessageLogService mongoMessageLogService) {
        return new MongoMessageOperator(applicationProperties, mongoMessageLogService);
    }
}
